﻿#pragma once
#ifndef MPMCQueue_hpp
#define MPMCQueue_hpp

#include"Mutex.hpp"
#include"Lock.hpp"
#include"SpinWaitLock.hpp"


namespace MMF{

    //多生产多消费队列
    template<typename T>
    class MPMCQueue
    {

        T* mDatas;                      ///< 数据指针
        size_t mCapacity;               ///< 数据容量
        std::atomic<size_t> mFront;     ///< 头指针
        char mTmp0[256];                ///< 保留不使用，用于将catch_line隔开，防止使用相同的总线锁，缓存锁
        std::atomic<size_t> mTail;      ///< 尾指针
        char mTmp1[256];                ///< 保留不使用，用于将catch_line隔开，防止使用相同的总线锁，缓存锁
        std::atomic<size_t> mCount;     ///< 当前数量

        ///< 由于是多生产多消费，无论如何这里都会有竞争问题
        ///< 尽量将竞争缩小范围
        SpinWaitLock mPopLock;          
        SpinWaitLock mPushLock;

    public:

        MPMCQueue(size_t capacity)
            : mCapacity(capacity)
            , mCount(0)

        {
            mDatas = new T[capacity];
            mFront = 0;
            mTail = 0;
        }

        ~MPMCQueue()
        {
            delete[] mDatas;
            mDatas = nullptr;
        }

        bool Push(const T& val)
        {

            //加锁使可以并发
            mPushLock.Lock();

            //如果数量已经push满，就休眠等到不满的时候
            std::chrono::microseconds start = TimeStamp::GetUsTime();
            while (mCount == mCapacity)
            {
                size_t old = mCapacity;

                //自旋一段时间
                std::chrono::microseconds cur{0};
                while(mCount == old && (cur - start).count() < 1000)
                    cur = TimeStamp::GetUsTime();

                mCount.wait(old, std::memory_order_acquire);
            }

            size_t target = mFront.load();
            mFront++;
            mFront = mFront % mCapacity;

            //插入值
            mDatas[target] = val;
            mCount++;
            mCount.notify_one();

            mPushLock.Unlock();

            return true;
        }

        bool Push(const T&& val)
        {
            //加锁使可以并发
            mPushLock.Lock();

            //如果数量已经push满，就休眠等到不满的时候
            std::chrono::microseconds start = TimeStamp::GetUsTime();
            while (mCount == mCapacity)
            {
                size_t old = mCapacity;

                //自旋一段时间
                std::chrono::microseconds cur{0};
                while(mCount == old && (cur - start).count() < 1000)
                    cur = TimeStamp::GetUsTime();

                mCount.wait(old, std::memory_order_acquire);
            }

            size_t target = mFront.load();
            mFront++;
            mFront = mFront % mCapacity;

            //插入值
            mDatas[target] = val;
            mCount++;
            mCount.notify_one();

            mPushLock.Unlock();
            
            return true;
        }

        bool Pop(T& val)
        {
            //加锁使可以并发
            mPopLock.Lock();

            //如果数量已经pop空，就休眠等到不为空的时候
            std::chrono::microseconds start = TimeStamp::GetUsTime();
            while (mCount == 0)
            {
                size_t old = 0;

                //自旋一段时间
                std::chrono::microseconds cur{0};
                while(mCount == old && (cur - start).count() < 1000)
                    cur = TimeStamp::GetUsTime();

                mCount.wait(old, std::memory_order_acquire);
            }

            size_t target = mTail.load();
            mTail++;
            mTail = mTail % mCapacity;

            //取出值
            val = std::move(mDatas[target]);
            mCount--;
            mCount.notify_one();

            mPopLock.Unlock();

            return true;
        }

        bool Back(T& val)
        {
            if (mCount == 0)
                return false;
                
            val = mDatas[mTail];
            return true;
        }

        bool Empty()
        {
            return mCount == 0;
        }

        size_t Size()
        {
            return mCapacity;
        }

        size_t Count()
        {
            return mCount.load();
        }
    };

}


#endif